/*
 * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
/*
 * Copyright 2002-2005 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.sun.org.apache.xerces.internal.impl.xs;

import java.lang.reflect.Array;
import java.util.AbstractList;
import java.util.Iterator;
import java.util.ListIterator;
import java.util.NoSuchElementException;
import java.util.Vector;

import com.sun.org.apache.xerces.internal.impl.Constants;
import com.sun.org.apache.xerces.internal.impl.xs.util.StringListImpl;
import com.sun.org.apache.xerces.internal.impl.xs.util.XSNamedMap4Types;
import com.sun.org.apache.xerces.internal.impl.xs.util.XSNamedMapImpl;
import com.sun.org.apache.xerces.internal.impl.xs.util.XSObjectListImpl;
import com.sun.org.apache.xerces.internal.util.SymbolHash;
import com.sun.org.apache.xerces.internal.util.XMLSymbols;
import com.sun.org.apache.xerces.internal.xs.StringList;
import com.sun.org.apache.xerces.internal.xs.XSAttributeDeclaration;
import com.sun.org.apache.xerces.internal.xs.XSAttributeGroupDefinition;
import com.sun.org.apache.xerces.internal.xs.XSConstants;
import com.sun.org.apache.xerces.internal.xs.XSElementDeclaration;
import com.sun.org.apache.xerces.internal.xs.XSModel;
import com.sun.org.apache.xerces.internal.xs.XSModelGroupDefinition;
import com.sun.org.apache.xerces.internal.xs.XSNamedMap;
import com.sun.org.apache.xerces.internal.xs.XSNamespaceItem;
import com.sun.org.apache.xerces.internal.xs.XSNamespaceItemList;
import com.sun.org.apache.xerces.internal.xs.XSNotationDeclaration;
import com.sun.org.apache.xerces.internal.xs.XSObject;
import com.sun.org.apache.xerces.internal.xs.XSObjectList;
import com.sun.org.apache.xerces.internal.xs.XSTypeDefinition;

/**
 * Implements XSModel:  a read-only interface that represents an XML Schema,
 * which could be components from different namespaces.
 *
 * @author Sandy Gao, IBM
 * @version $Id: XSModelImpl.java,v 1.7 2010-11-01 04:39:55 joehw Exp $
 * @xerces.internal
 */
public final class XSModelImpl extends AbstractList implements XSModel, XSNamespaceItemList {

  // the max index / the max value of XSObject type
  private static final short MAX_COMP_IDX = XSTypeDefinition.SIMPLE_TYPE;
  private static final boolean[] GLOBAL_COMP = {false,    // null
      true,     // attribute
      true,     // element
      true,     // type
      false,    // attribute use
      true,     // attribute group
      true,     // group
      false,    // model group
      false,    // particle
      false,    // wildcard
      false,    // idc
      true,     // notation
      false,    // annotation
      false,    // facet
      false,    // multi value facet
      true,     // complex type
      true      // simple type
  };

  // number of grammars/namespaces stored here
  private final int fGrammarCount;
  // all target namespaces
  private final String[] fNamespaces;
  // all schema grammar objects (for each namespace)
  private final SchemaGrammar[] fGrammarList;
  // a map from namespace to schema grammar
  private final SymbolHash fGrammarMap;
  // a map from element declaration to its substitution group
  private final SymbolHash fSubGroupMap;

  // store a certain kind of components from all namespaces
  private final XSNamedMap[] fGlobalComponents;
  // store a certain kind of components from one namespace
  private final XSNamedMap[][] fNSComponents;

  // a string list of all the target namespaces.
  private final StringList fNamespacesList;
  // store all annotations
  private XSObjectList fAnnotations = null;

  // whether there is any IDC in this XSModel
  private final boolean fHasIDC;

  /**
   * Construct an XSModelImpl, by storing some grammars and grammars imported
   * by them to this object.
   *
   * @param grammars the array of schema grammars
   */
  public XSModelImpl(SchemaGrammar[] grammars) {
    this(grammars, Constants.SCHEMA_VERSION_1_0);
  }

  public XSModelImpl(SchemaGrammar[] grammars, short s4sVersion) {
    // copy namespaces/grammars from the array to our arrays
    int len = grammars.length;
    final int initialSize = Math.max(len + 1, 5);
    String[] namespaces = new String[initialSize];
    SchemaGrammar[] grammarList = new SchemaGrammar[initialSize];
    boolean hasS4S = false;
    for (int i = 0; i < len; i++) {
      final SchemaGrammar sg = grammars[i];
      final String tns = sg.getTargetNamespace();
      namespaces[i] = tns;
      grammarList[i] = sg;
      if (tns == SchemaSymbols.URI_SCHEMAFORSCHEMA) {
        hasS4S = true;
      }
    }
    // If a schema for the schema namespace isn't included, include it here.
    if (!hasS4S) {
      namespaces[len] = SchemaSymbols.URI_SCHEMAFORSCHEMA;
      grammarList[len++] = SchemaGrammar.getS4SGrammar(s4sVersion);
    }

    SchemaGrammar sg1, sg2;
    Vector gs;
    int i, j, k;
    // and recursively get all imported grammars, add them to our arrays
    for (i = 0; i < len; i++) {
      // get the grammar
      sg1 = grammarList[i];
      gs = sg1.getImportedGrammars();
      // for each imported grammar
      for (j = gs == null ? -1 : gs.size() - 1; j >= 0; j--) {
        sg2 = (SchemaGrammar) gs.elementAt(j);
        // check whether this grammar is already in the list
        for (k = 0; k < len; k++) {
          if (sg2 == grammarList[k]) {
            break;
          }
        }
        // if it's not, add it to the list
        if (k == len) {
          // ensure the capacity of the arrays
          if (len == grammarList.length) {
            String[] newSA = new String[len * 2];
            System.arraycopy(namespaces, 0, newSA, 0, len);
            namespaces = newSA;
            SchemaGrammar[] newGA = new SchemaGrammar[len * 2];
            System.arraycopy(grammarList, 0, newGA, 0, len);
            grammarList = newGA;
          }
          namespaces[len] = sg2.getTargetNamespace();
          grammarList[len] = sg2;
          len++;
        }
      }
    }

    fNamespaces = namespaces;
    fGrammarList = grammarList;

    boolean hasIDC = false;
    // establish the mapping from namespace to grammars
    fGrammarMap = new SymbolHash(len * 2);
    for (i = 0; i < len; i++) {
      fGrammarMap.put(null2EmptyString(fNamespaces[i]), fGrammarList[i]);
      // update the idc field
      if (fGrammarList[i].hasIDConstraints()) {
        hasIDC = true;
      }
    }

    fHasIDC = hasIDC;
    fGrammarCount = len;
    fGlobalComponents = new XSNamedMap[MAX_COMP_IDX + 1];
    fNSComponents = new XSNamedMap[len][MAX_COMP_IDX + 1];
    fNamespacesList = new StringListImpl(fNamespaces, fGrammarCount);

    // build substitution groups
    fSubGroupMap = buildSubGroups();
  }

  private SymbolHash buildSubGroups_Org() {
    SubstitutionGroupHandler sgHandler = new SubstitutionGroupHandler(null);
    for (int i = 0; i < fGrammarCount; i++) {
      sgHandler.addSubstitutionGroup(fGrammarList[i].getSubstitutionGroups());
    }

    final XSNamedMap elements = getComponents(XSConstants.ELEMENT_DECLARATION);
    final int len = elements.getLength();
    final SymbolHash subGroupMap = new SymbolHash(len * 2);
    XSElementDecl head;
    XSElementDeclaration[] subGroup;
    for (int i = 0; i < len; i++) {
      head = (XSElementDecl) elements.item(i);
      subGroup = sgHandler.getSubstitutionGroup(head);
      subGroupMap.put(head, subGroup.length > 0 ?
          new XSObjectListImpl(subGroup, subGroup.length) : XSObjectListImpl.EMPTY_LIST);
    }
    return subGroupMap;
  }

  private SymbolHash buildSubGroups() {
    SubstitutionGroupHandler sgHandler = new SubstitutionGroupHandler(null);
    for (int i = 0; i < fGrammarCount; i++) {
      sgHandler.addSubstitutionGroup(fGrammarList[i].getSubstitutionGroups());
    }

    final XSObjectListImpl elements = getGlobalElements();
    final int len = elements.getLength();
    final SymbolHash subGroupMap = new SymbolHash(len * 2);
    XSElementDecl head;
    XSElementDeclaration[] subGroup;
    for (int i = 0; i < len; i++) {
      head = (XSElementDecl) elements.item(i);
      subGroup = sgHandler.getSubstitutionGroup(head);
      subGroupMap.put(head, subGroup.length > 0 ?
          new XSObjectListImpl(subGroup, subGroup.length) : XSObjectListImpl.EMPTY_LIST);
    }
    return subGroupMap;
  }

  private XSObjectListImpl getGlobalElements() {
    final SymbolHash[] tables = new SymbolHash[fGrammarCount];
    int length = 0;

    for (int i = 0; i < fGrammarCount; i++) {
      tables[i] = fGrammarList[i].fAllGlobalElemDecls;
      length += tables[i].getLength();
    }

    if (length == 0) {
      return XSObjectListImpl.EMPTY_LIST;
    }

    final XSObject[] components = new XSObject[length];

    int start = 0;
    for (int i = 0; i < fGrammarCount; i++) {
      tables[i].getValues(components, start);
      start += tables[i].getLength();
    }

    return new XSObjectListImpl(components, length);
  }

  /**
   * Convenience method. Returns a list of all namespaces that belong to
   * this schema.
   *
   * @return A list of all namespaces that belong to this schema or <code>null</code> if all
   * components don't have a targetNamespace.
   */
  public StringList getNamespaces() {
    return fNamespacesList;
  }

  /**
   * A set of namespace schema information information items (of type
   * <code>XSNamespaceItem</code>), one for each namespace name which
   * appears as the target namespace of any schema component in the schema
   * used for that assessment, and one for absent if any schema component
   * in the schema had no target namespace. For more information see
   * schema information.
   */
  public XSNamespaceItemList getNamespaceItems() {
    return this;
  }

  /**
   * Returns a list of top-level components, i.e. element declarations,
   * attribute declarations, etc.
   *
   * @param objectType The type of the declaration, i.e. <code>ELEMENT_DECLARATION</code>. Note that
   * <code>XSTypeDefinition.SIMPLE_TYPE</code> and <code>XSTypeDefinition.COMPLEX_TYPE</code> can
   * also be used as the <code>objectType</code> to retrieve only complex types or simple types,
   * instead of all types.
   * @return A list of top-level definitions of the specified type in <code>objectType</code> or an
   * empty <code>XSNamedMap</code> if no such definitions exist.
   */
  public synchronized XSNamedMap getComponents(short objectType) {
    if (objectType <= 0 || objectType > MAX_COMP_IDX ||
        !GLOBAL_COMP[objectType]) {
      return XSNamedMapImpl.EMPTY_MAP;
    }

    SymbolHash[] tables = new SymbolHash[fGrammarCount];
    // get all hashtables from all namespaces for this type of components
    if (fGlobalComponents[objectType] == null) {
      for (int i = 0; i < fGrammarCount; i++) {
        switch (objectType) {
          case XSConstants.TYPE_DEFINITION:
          case XSTypeDefinition.COMPLEX_TYPE:
          case XSTypeDefinition.SIMPLE_TYPE:
            tables[i] = fGrammarList[i].fGlobalTypeDecls;
            break;
          case XSConstants.ATTRIBUTE_DECLARATION:
            tables[i] = fGrammarList[i].fGlobalAttrDecls;
            break;
          case XSConstants.ELEMENT_DECLARATION:
            tables[i] = fGrammarList[i].fGlobalElemDecls;
            break;
          case XSConstants.ATTRIBUTE_GROUP:
            tables[i] = fGrammarList[i].fGlobalAttrGrpDecls;
            break;
          case XSConstants.MODEL_GROUP_DEFINITION:
            tables[i] = fGrammarList[i].fGlobalGroupDecls;
            break;
          case XSConstants.NOTATION_DECLARATION:
            tables[i] = fGrammarList[i].fGlobalNotationDecls;
            break;
        }
      }
      // for complex/simple types, create a special implementation,
      // which take specific types out of the hash table
      if (objectType == XSTypeDefinition.COMPLEX_TYPE ||
          objectType == XSTypeDefinition.SIMPLE_TYPE) {
        fGlobalComponents[objectType] = new XSNamedMap4Types(fNamespaces, tables, fGrammarCount,
            objectType);
      } else {
        fGlobalComponents[objectType] = new XSNamedMapImpl(fNamespaces, tables, fGrammarCount);
      }
    }

    return fGlobalComponents[objectType];
  }

  /**
   * Convenience method. Returns a list of top-level component declarations
   * that are defined within the specified namespace, i.e. element
   * declarations, attribute declarations, etc.
   *
   * @param objectType The type of the declaration, i.e. <code>ELEMENT_DECLARATION</code>.
   * @param namespace The namespace to which the declaration belongs or <code>null</code> (for
   * components with no target namespace).
   * @return A list of top-level definitions of the specified type in <code>objectType</code> and
   * defined in the specified <code>namespace</code> or an empty <code>XSNamedMap</code>.
   */
  public synchronized XSNamedMap getComponentsByNamespace(short objectType,
      String namespace) {
    if (objectType <= 0 || objectType > MAX_COMP_IDX ||
        !GLOBAL_COMP[objectType]) {
      return XSNamedMapImpl.EMPTY_MAP;
    }

    // try to find the grammar
    int i = 0;
    if (namespace != null) {
      for (; i < fGrammarCount; ++i) {
        if (namespace.equals(fNamespaces[i])) {
          break;
        }
      }
    } else {
      for (; i < fGrammarCount; ++i) {
        if (fNamespaces[i] == null) {
          break;
        }
      }
    }
    if (i == fGrammarCount) {
      return XSNamedMapImpl.EMPTY_MAP;
    }

    // get the hashtable for this type of components
    if (fNSComponents[i][objectType] == null) {
      SymbolHash table = null;
      switch (objectType) {
        case XSConstants.TYPE_DEFINITION:
        case XSTypeDefinition.COMPLEX_TYPE:
        case XSTypeDefinition.SIMPLE_TYPE:
          table = fGrammarList[i].fGlobalTypeDecls;
          break;
        case XSConstants.ATTRIBUTE_DECLARATION:
          table = fGrammarList[i].fGlobalAttrDecls;
          break;
        case XSConstants.ELEMENT_DECLARATION:
          table = fGrammarList[i].fGlobalElemDecls;
          break;
        case XSConstants.ATTRIBUTE_GROUP:
          table = fGrammarList[i].fGlobalAttrGrpDecls;
          break;
        case XSConstants.MODEL_GROUP_DEFINITION:
          table = fGrammarList[i].fGlobalGroupDecls;
          break;
        case XSConstants.NOTATION_DECLARATION:
          table = fGrammarList[i].fGlobalNotationDecls;
          break;
      }

      // for complex/simple types, create a special implementation,
      // which take specific types out of the hash table
      if (objectType == XSTypeDefinition.COMPLEX_TYPE ||
          objectType == XSTypeDefinition.SIMPLE_TYPE) {
        fNSComponents[i][objectType] = new XSNamedMap4Types(namespace, table, objectType);
      } else {
        fNSComponents[i][objectType] = new XSNamedMapImpl(namespace, table);
      }
    }

    return fNSComponents[i][objectType];
  }

  /**
   * Convenience method. Returns a top-level simple or complex type
   * definition.
   *
   * @param name The name of the definition.
   * @param namespace The namespace of the definition, otherwise null.
   * @return An <code>XSTypeDefinition</code> or null if such definition does not exist.
   */
  public XSTypeDefinition getTypeDefinition(String name,
      String namespace) {
    SchemaGrammar sg = (SchemaGrammar) fGrammarMap.get(null2EmptyString(namespace));
    if (sg == null) {
      return null;
    }
    return (XSTypeDefinition) sg.fGlobalTypeDecls.get(name);
  }

  /**
   * Convenience method. Returns a top-level simple or complex type
   * definition.
   *
   * @param name The name of the definition.
   * @param namespace The namespace of the definition, otherwise null.
   * @param loc The schema location where the component was defined
   * @return An <code>XSTypeDefinition</code> or null if such definition does not exist.
   */
  public XSTypeDefinition getTypeDefinition(String name,
      String namespace,
      String loc) {
    SchemaGrammar sg = (SchemaGrammar) fGrammarMap.get(null2EmptyString(namespace));
    if (sg == null) {
      return null;
    }
    return sg.getGlobalTypeDecl(name, loc);
  }

  /**
   * Convenience method. Returns a top-level attribute declaration.
   *
   * @param name The name of the declaration.
   * @param namespace The namespace of the definition, otherwise null.
   * @return A top-level attribute declaration or null if such declaration does not exist.
   */
  public XSAttributeDeclaration getAttributeDeclaration(String name,
      String namespace) {
    SchemaGrammar sg = (SchemaGrammar) fGrammarMap.get(null2EmptyString(namespace));
    if (sg == null) {
      return null;
    }
    return (XSAttributeDeclaration) sg.fGlobalAttrDecls.get(name);
  }

  /**
   * Convenience method. Returns a top-level attribute declaration.
   *
   * @param name The name of the declaration.
   * @param namespace The namespace of the definition, otherwise null.
   * @param loc The schema location where the component was defined
   * @return A top-level attribute declaration or null if such declaration does not exist.
   */
  public XSAttributeDeclaration getAttributeDeclaration(String name,
      String namespace,
      String loc) {
    SchemaGrammar sg = (SchemaGrammar) fGrammarMap.get(null2EmptyString(namespace));
    if (sg == null) {
      return null;
    }
    return sg.getGlobalAttributeDecl(name, loc);
  }

  /**
   * Convenience method. Returns a top-level element declaration.
   *
   * @param name The name of the declaration.
   * @param namespace The namespace of the definition, otherwise null.
   * @return A top-level element declaration or null if such declaration does not exist.
   */
  public XSElementDeclaration getElementDeclaration(String name,
      String namespace) {
    SchemaGrammar sg = (SchemaGrammar) fGrammarMap.get(null2EmptyString(namespace));
    if (sg == null) {
      return null;
    }
    return (XSElementDeclaration) sg.fGlobalElemDecls.get(name);
  }

  /**
   * Convenience method. Returns a top-level element declaration.
   *
   * @param name The name of the declaration.
   * @param namespace The namespace of the definition, otherwise null.
   * @param loc The schema location where the component was defined
   * @return A top-level element declaration or null if such declaration does not exist.
   */
  public XSElementDeclaration getElementDeclaration(String name,
      String namespace,
      String loc) {
    SchemaGrammar sg = (SchemaGrammar) fGrammarMap.get(null2EmptyString(namespace));
    if (sg == null) {
      return null;
    }
    return sg.getGlobalElementDecl(name, loc);
  }

  /**
   * Convenience method. Returns a top-level attribute group definition.
   *
   * @param name The name of the definition.
   * @param namespace The namespace of the definition, otherwise null.
   * @return A top-level attribute group definition or null if such definition does not exist.
   */
  public XSAttributeGroupDefinition getAttributeGroup(String name,
      String namespace) {
    SchemaGrammar sg = (SchemaGrammar) fGrammarMap.get(null2EmptyString(namespace));
    if (sg == null) {
      return null;
    }
    return (XSAttributeGroupDefinition) sg.fGlobalAttrGrpDecls.get(name);
  }

  /**
   * Convenience method. Returns a top-level attribute group definition.
   *
   * @param name The name of the definition.
   * @param namespace The namespace of the definition, otherwise null.
   * @param loc The schema location where the component was defined
   * @return A top-level attribute group definition or null if such definition does not exist.
   */
  public XSAttributeGroupDefinition getAttributeGroup(String name,
      String namespace,
      String loc) {
    SchemaGrammar sg = (SchemaGrammar) fGrammarMap.get(null2EmptyString(namespace));
    if (sg == null) {
      return null;
    }
    return sg.getGlobalAttributeGroupDecl(name, loc);
  }

  /**
   * Convenience method. Returns a top-level model group definition.
   *
   * @param name The name of the definition.
   * @param namespace The namespace of the definition, otherwise null.
   * @return A top-level model group definition definition or null if such definition does not
   * exist.
   */
  public XSModelGroupDefinition getModelGroupDefinition(String name,
      String namespace) {
    SchemaGrammar sg = (SchemaGrammar) fGrammarMap.get(null2EmptyString(namespace));
    if (sg == null) {
      return null;
    }
    return (XSModelGroupDefinition) sg.fGlobalGroupDecls.get(name);
  }

  /**
   * Convenience method. Returns a top-level model group definition.
   *
   * @param name The name of the definition.
   * @param namespace The namespace of the definition, otherwise null.
   * @param loc The schema location where the component was defined
   * @return A top-level model group definition definition or null if such definition does not
   * exist.
   */
  public XSModelGroupDefinition getModelGroupDefinition(String name,
      String namespace,
      String loc) {
    SchemaGrammar sg = (SchemaGrammar) fGrammarMap.get(null2EmptyString(namespace));
    if (sg == null) {
      return null;
    }
    return sg.getGlobalGroupDecl(name, loc);
  }


  /**
   * @see org.apache.xerces.xs.XSModel#getNotationDeclaration(String, String)
   */
  public XSNotationDeclaration getNotationDeclaration(String name,
      String namespace) {
    SchemaGrammar sg = (SchemaGrammar) fGrammarMap.get(null2EmptyString(namespace));
    if (sg == null) {
      return null;
    }
    return (XSNotationDeclaration) sg.fGlobalNotationDecls.get(name);
  }

  public XSNotationDeclaration getNotationDeclaration(String name,
      String namespace,
      String loc) {
    SchemaGrammar sg = (SchemaGrammar) fGrammarMap.get(null2EmptyString(namespace));
    if (sg == null) {
      return null;
    }
    return sg.getGlobalNotationDecl(name, loc);
  }

  /**
   * [annotations]: a set of annotations if it exists, otherwise an empty
   * <code>XSObjectList</code>.
   */
  public synchronized XSObjectList getAnnotations() {
    if (fAnnotations != null) {
      return fAnnotations;
    }

    // do this in two passes to avoid inaccurate array size
    int totalAnnotations = 0;
    for (int i = 0; i < fGrammarCount; i++) {
      totalAnnotations += fGrammarList[i].fNumAnnotations;
    }
    if (totalAnnotations == 0) {
      fAnnotations = XSObjectListImpl.EMPTY_LIST;
      return fAnnotations;
    }
    XSAnnotationImpl[] annotations = new XSAnnotationImpl[totalAnnotations];
    int currPos = 0;
    for (int i = 0; i < fGrammarCount; i++) {
      SchemaGrammar currGrammar = fGrammarList[i];
      if (currGrammar.fNumAnnotations > 0) {
        System.arraycopy(currGrammar.fAnnotations, 0, annotations, currPos,
            currGrammar.fNumAnnotations);
        currPos += currGrammar.fNumAnnotations;
      }
    }
    fAnnotations = new XSObjectListImpl(annotations, annotations.length);
    return fAnnotations;
  }

  private static final String null2EmptyString(String str) {
    return str == null ? XMLSymbols.EMPTY_STRING : str;
  }

  /**
   * REVISIT: to expose identity constraints from XSModel.
   * For now, we only expose whether there are any IDCs.
   * We also need to add these methods to the public
   * XSModel interface.
   */
  public boolean hasIDConstraints() {
    return fHasIDC;
  }

  /**
   * Convenience method. Returns a list containing the members of the
   * substitution group for the given <code>XSElementDeclaration</code>
   * or an empty <code>XSObjectList</code> if the substitution group
   * contains no members.
   *
   * @param head The substitution group head.
   * @return A list containing the members of the substitution group for the given
   * <code>XSElementDeclaration</code> or an empty <code>XSObjectList</code> if the substitution
   * group contains no members.
   */
  public XSObjectList getSubstitutionGroup(XSElementDeclaration head) {
    return (XSObjectList) fSubGroupMap.get(head);
  }

  //
  // XSNamespaceItemList methods
  //

  /**
   * The number of <code>XSNamespaceItem</code>s in the list. The range of
   * valid child object indices is 0 to <code>length-1</code> inclusive.
   */
  public int getLength() {
    return fGrammarCount;
  }

  /**
   * Returns the <code>index</code>th item in the collection or
   * <code>null</code> if <code>index</code> is greater than or equal to
   * the number of objects in the list. The index starts at 0.
   *
   * @param index index into the collection.
   * @return The <code>XSNamespaceItem</code> at the <code>index</code>th position in the
   * <code>XSNamespaceItemList</code>, or <code>null</code> if the index specified is not valid.
   */
  public XSNamespaceItem item(int index) {
    if (index < 0 || index >= fGrammarCount) {
      return null;
    }
    return fGrammarList[index];
  }

  //
  // java.util.List methods
  //

  public Object get(int index) {
    if (index >= 0 && index < fGrammarCount) {
      return fGrammarList[index];
    }
    throw new IndexOutOfBoundsException("Index: " + index);
  }

  public int size() {
    return getLength();
  }

  public Iterator iterator() {
    return listIterator0(0);
  }

  public ListIterator listIterator() {
    return listIterator0(0);
  }

  public ListIterator listIterator(int index) {
    if (index >= 0 && index < fGrammarCount) {
      return listIterator0(index);
    }
    throw new IndexOutOfBoundsException("Index: " + index);
  }

  private ListIterator listIterator0(int index) {
    return new XSNamespaceItemListIterator(index);
  }

  public Object[] toArray() {
    Object[] a = new Object[fGrammarCount];
    toArray0(a);
    return a;
  }

  public Object[] toArray(Object[] a) {
    if (a.length < fGrammarCount) {
      Class arrayClass = a.getClass();
      Class componentType = arrayClass.getComponentType();
      a = (Object[]) Array.newInstance(componentType, fGrammarCount);
    }
    toArray0(a);
    if (a.length > fGrammarCount) {
      a[fGrammarCount] = null;
    }
    return a;
  }

  private void toArray0(Object[] a) {
    if (fGrammarCount > 0) {
      System.arraycopy(fGrammarList, 0, a, 0, fGrammarCount);
    }
  }

  private final class XSNamespaceItemListIterator implements ListIterator {

    private int index;

    public XSNamespaceItemListIterator(int index) {
      this.index = index;
    }

    public boolean hasNext() {
      return (index < fGrammarCount);
    }

    public Object next() {
      if (index < fGrammarCount) {
        return fGrammarList[index++];
      }
      throw new NoSuchElementException();
    }

    public boolean hasPrevious() {
      return (index > 0);
    }

    public Object previous() {
      if (index > 0) {
        return fGrammarList[--index];
      }
      throw new NoSuchElementException();
    }

    public int nextIndex() {
      return index;
    }

    public int previousIndex() {
      return index - 1;
    }

    public void remove() {
      throw new UnsupportedOperationException();
    }

    public void set(Object o) {
      throw new UnsupportedOperationException();
    }

    public void add(Object o) {
      throw new UnsupportedOperationException();
    }
  }

} // class XSModelImpl
